package cas.server.login;

import cas.client.User;
import cas.server.DB;
import cas.server.SecurityKey;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;


@WebServlet(name = "LoginAPIServlet", urlPatterns = "/login-api")
public class LoginAPIServlet extends HttpServlet {

    private static final String DEFAULT_RETURN_ADDRESS = "/";

    // 验证账号和密码，生成token，放入用户的cookie中
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            User user = objectMapper.readValue(request.getInputStream(), User.class);
            if (authenticateSubject(user)) { // 验证账号和密码
                Timestamp issuedAt = Timestamp.valueOf(LocalDateTime.now());
                Timestamp expiration = Timestamp.from(issuedAt.toInstant().plus(2, ChronoUnit.HOURS));
                // 生成token
                String token = generateToken(user, issuedAt, expiration);
                // 存储token到数据库
                storeToken(user, issuedAt, expiration, token);
                // 把token放入用户的cookie中
                Cookie cookie = generateTokenCookie(token);
                response.addCookie(cookie);
                // 把token作为参数，跳转返回
                jumpBack(request, response, token);
            }
        } catch (SQLException e) {
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }
    }

    // 验证用户身份
    private static boolean authenticateSubject(User user) throws SQLException {
        boolean[] result = {false};
        DB.query(resultSet -> result[0] = resultSet.next(), "SELECT account FROM user WHERE account = ? AND password = ?", user.getAccount(), user.getPassword());
        return result[0];
    }

    // 生成token
    private static String generateToken(User user, Timestamp issuedAt, Timestamp expiration) {
        // 设置JWT的负载信息
        Claims claims = Jwts.claims()
                .subject(user.getAccount())
                .issuedAt(issuedAt)
                .expiration(expiration)
                .build();

        // 生成JWT
        String jwt = Jwts.builder()
                .claims(claims)
                .signWith(SecurityKey.getKeyPair().getPrivate())
                .compact();

        return jwt;
    }

    // 存储token到数据库
    private static void storeToken(User user, Timestamp issuedAt, Timestamp expiration, String token) throws SQLException {
        DB.update(affectedRows -> {}, "INSERT INTO token(account, issued_at, expiration) VALUES(?, ?, ?) ON DUPLICATE KEY UPDATE issued_at = VALUES(issued_at), expiration = VALUES(expiration)", user.getAccount(), issuedAt, expiration);
    }

    // 把token添加到用户的cookie里
    private static Cookie generateTokenCookie(String token) {
        Cookie cookie = new Cookie("token", token);
        cookie.setPath("/");
        cookie.setHttpOnly(true);
//        cookie.setSecure(true);
        cookie.setMaxAge(60 * 60);
        return cookie;
    }

    // 把token作为参数，跳转返回
    private static void jumpBack(HttpServletRequest request, HttpServletResponse response, String token) throws IOException {
        // 获取返回地址
        String returnAddress;
        if (request.getSession().getAttribute("returnAddress") == null ) {
            if (request.getParameter("return-address") == null) {
                returnAddress = DEFAULT_RETURN_ADDRESS;
            } else {
                returnAddress = URLDecoder.decode(request.getParameter("return-address"), "UTF-8");
            }
        } else {
            returnAddress = URLDecoder.decode((String) request.getSession().getAttribute("returnAddress"), "UTF-8");
        }
        // 插入token作为参数
        String returnAddress_;
        if(returnAddress.contains("?")) {
            returnAddress_ = returnAddress + "&token=" + token;
        } else {
            returnAddress_ = returnAddress + "?token=" + token;
        }
        // 响应
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.writeValue(response.getOutputStream(), new Object() {
            private String returnAddress = returnAddress_;

            public String getReturnAddress() {
                return returnAddress;
            }

            public void setReturnAddress(String returnAddress) {
                this.returnAddress = returnAddress;
            }
        });
    }
}
